#include "HTMLizer.h"
#include<iostream>
#include<sstream>

using namespace std;

bool HTMLizer::isKeyword(const string& s)
{
	//TODO: Change to map-based
	static const char* asKeywords[] = {
		"and",
		"and_eq",
		"asm",
		"auto",
		"bitand",
		"bitor",
		"bool",
		"break",
		"case",
		"catch",
		"char",
		"class",
		"compl",
		"const",
		"const_cast",
		"continue",
		"default",
		"delete",
		"do",
		"double",
		"dynamic_cast",
		"else",
		"enum",
		"explicit",
		"export",
		"extern",
		"false",
		"float",
		"for",
		"friend",
		"goto",
		"if",
		"inline",
		"int",
		"long",
		"mutable",
		"namespace",
		"new",
		"not",
		"not_eq",
		"operator",
		"or",
		"or_eq",
		"private",
		"protected",
		"public",
		"register",
		"reinterpret_cast",
		"return",
		"short",
		"signed",
		"sizeof",
		"static",
		"static_cast",
		"struct",
		"switch",
		"template",
		"this",
		"throw",
		"true",
		"try",
		"typedef",
		"typeid",
		"typename",
		"union",
		"unsigned",
		"using",
		"virtual",
		"void",
		"volatile",
		"wchar_t",
		"while",
		"xor",
		"xor_eq"
	};

	for (int i = 0; i < sizeof(asKeywords) / sizeof(char*); i++)
		if (string(asKeywords[i]) == s)
			return true;

	return false;
}

bool HTMLizer::isPreprocessorKeyword (const string& s)
{
	//TODO: Change to map-based
	static const char* pptokens[] = {
		"define",
		"elif",
		"else",
		"endif",
		"error",
		"if",
		"ifdef",
		"ifndef",
		"include",
		"line",
		"pragma",
		"undef"
	};

	for (int i = 0; i < sizeof(pptokens) / sizeof(char*); i++)
		if (s.find(pptokens[i]) != string::npos)
			return true;

	return false;
}

HTMLizer::HTMLizer(void)
{
	m_pxCurrentScope = NULL;
	m_eScopeType = Scope::eScopeInvalid;
	m_nLineNumber = 1;
	m_cLookAhead = '\0';
}

HTMLizer::~HTMLizer(void)
{
}

void HTMLizer::onNewToken(Token& inToken)
{
	char cDelimiter= char(inToken);
	std::string sTokenHTML="";

	switch (cDelimiter) {
		case '&':
			sTokenHTML = "&amp;";
			break;
		case '<':
			sTokenHTML = "&lt;";
			break;
		case '>':
			sTokenHTML = "&gt;";
			break;
		case '"':
			sTokenHTML = "&quot;";
			break;
		case '\t':
			sTokenHTML = "&nbsp;&nbsp;&nbsp;&nbsp;"; 
			//TODO: Replace with string padding function
			break;
		case ' ':
			if(' ' == m_cLookAhead)
			{
				sTokenHTML = "&nbsp;";
			}
			else
			{
				sTokenHTML = inToken.m_sValue;
				m_cLookAhead = cDelimiter;
			}
			break;
		case '\r':
			sTokenHTML = ""; //Otherwise \r\n sequences will produce two <BR/>s 
			//TODO: Replace with lookahead strategy
			break;
		case '\n':
			sTokenHTML ="<BR/>\n"; // \n for human-readability of generated HTML
			break;
		default:
			sTokenHTML = inToken.m_sValue;
			break;
	}


	switch( m_eScopeType)
	{
	case Scope::eScopeCommentC:
	case Scope::eScopeCommentCPP:
	case Scope::eScopePreprocessor:
	case Scope::eScopeLiteral:
		break;

	default:
		if(isKeyword(sTokenHTML))
		{
			sTokenHTML = ("<span class=keyword>" + sTokenHTML + "</span>");
		}
	}

    cout<<sTokenHTML;

	if('\n' == cDelimiter)
		//Intentionally not coupling with switch case
	{
		m_nLineNumber++;
		createNewline();
	}

}

void HTMLizer::createNewline()
{
	createSpanEnd();

	stringstream sLineNumber;
	sLineNumber<<m_nLineNumber;
	std::string sLineHTML="";
	sLineHTML += "</scubaline>\n";
	sLineHTML += "<scubaline id= \"";
	sLineHTML += sLineNumber.str();
	sLineHTML += "\">";
	cout<<sLineHTML<<endl;

	createSpanForScope();

}

void HTMLizer::createSpanForScope()
{
	switch(m_eScopeType)
	{
	case Scope::eScopeCommentC:
	case Scope::eScopeCommentCPP:
		cout << "<span class=comment>";
		break;

	case Scope::eScopePreprocessor:
		cout << "<span class=preprocessor>";
		break;

	case Scope::eScopeLiteral:
		cout << "<span class=literal>";
		break;

	}
}

void HTMLizer::createSpanEnd()
{
	switch(m_eScopeType)
	{
	case Scope::eScopeCommentC:
	case Scope::eScopeCommentCPP:
	case Scope::eScopePreprocessor:
	case Scope::eScopeLiteral:
		cout<< "</span>";
		break;

	}
}

void HTMLizer::onScopeEntry(Scope& inNewScope, Scope& inOldScope)
{
	m_pxCurrentScope = &inNewScope;
	m_eScopeType = m_pxCurrentScope->getScopeType();
	createSpanForScope();
}

void HTMLizer::onScopeExit(Scope& inNewScope, Scope& inOldScope)
{
	createSpanEnd();
	m_pxCurrentScope = &inNewScope;
	m_eScopeType = m_pxCurrentScope->getScopeType();

}

void HTMLizer::generateHeader()
{
	cout << "<html>" << endl << "<head>" << endl;
	cout << "<style>" << endl;
	cout << "BODY{font-family:courier;font-size:8pt}"<<endl;
	cout << ".keyword{color:rgb(0,0,255);}" << endl;
	cout << ".comment{color:rgb(0,128,0);}" << endl;
	cout << ".preprocessor{color:rgb(0,0,255);}" << endl;
	cout << ".literal{color:rgb(163,21,21);}" << endl;
	cout << ".p{font-family:courier;font-size:8pt;}"<<endl;

//A:link{color:white}
//A:visited{color:yellow	
	  cout << "</style>" << endl << "<body>" << endl;
	  cout << "<scubaline id= \"1\">" << endl;

}

void HTMLizer::generateFooter()
{
	cout<<"</scubaline>"<<endl;
	cout << "</body>" << endl << "</html>" << endl;
}

